When writing code during the development of software applications, developers commonly spend a significant amount of time “debugging” the code to find runtime errors in the code. In doing so, developers may take several approaches to reproduce and localize a source code bug, such as observing behavior of a program based on different inputs, inserting debugging code (e.g., to print variable values, to track branches of execution, etc.), temporarily removing code portions, etc. Tracking down runtime errors to pinpoint code bugs can occupy a significant portion of application development time.
Many types of debugging applications (“debuggers”) have been developed in order to assist developers with the code debugging process. Many such tools offer developers the ability to trace, visualize, and alter the execution of computer code. For example, debuggers may visualize the execution of code instructions, may present variable values at various times during code execution, may enable developers to alter code execution paths, and/or may enable developers to set “breakpoints” in code (which, when reached during execution, causes execution of the code to be suspended), among other things.
An emerging form of debugging applications enable “time travel,” “reverse,” or “historic” debugging, in which execution of a program is recorded/traced by a trace application into one or more trace files, which can be then be used to replay execution of the program for forward and backward analysis. For example, time travel debuggers can enable a developer to set forward breakpoints (like conventional debuggers) as well as reverse breakpoints. However, to ensure correctness of trace data, these conventional time travel debuggers enforce restrictions in which program code be traced as if it were single-threaded and executing on a single processor—even if the code was written to be multi-threaded. This, in turn, leads to slow replay performance of these traces, since replay is likewise single-threaded.